#include "StdAfx.h"
#include "db.h"
#include "httpengine.h"

QMap<QChar, QString> Db::DbString::m_patternMap;

Db::DbString::DbString( const QString & text )
:   QString( text )
{
    if (m_patternMap.isEmpty())
    {
        m_patternMap.insert( QChar( '\0' ), "\\0" );
        m_patternMap.insert( QChar( '\'' ), "\\'" );
        m_patternMap.insert( QChar( '\"' ), "\\\"" );
        m_patternMap.insert( QChar( '\b' ), "\\b" );
        // treba to este odladit
        m_patternMap.insert( QChar( '\n' ), "\\n" );
        m_patternMap.insert( QChar( '\r' ), "\\r" );
        m_patternMap.insert( QChar( '\t' ), "\\t" );
        m_patternMap.insert( QChar( '\Z' ), "\\Z" );
        m_patternMap.insert( QChar( '\\' ), "\\\\" );
    }
}

Db::DbString::~DbString()
{
}

Db::DbString & Db::DbString::operator=( const DbString & dbString )
{
    QString::operator=( dbString );
    return * this;
}

Db::DbString Db::DbString::argValue( const int & value )
{
    return arg( value );
}

Db::DbString Db::DbString::argValue( const double & value )
{
    return arg( value );
}

Db::DbString Db::DbString::argValue( const QDate & value )
{
    QString outValueStr( "'" );
    outValueStr.append( value.toString( Qt::ISODate ) );
    outValueStr.append( "'" );
    return arg( outValueStr );
}

Db::DbString Db::DbString::argValue( const QDateTime & value )
{
    QString dateTimeStr = value.toString( Qt::ISODate );
    dateTimeStr.replace( QChar( 'T' ), " " );
    QString outValueStr( "'" );
    outValueStr.append( dateTimeStr );
    outValueStr.append( "'" );
    return arg( outValueStr );
}

Db::DbString Db::DbString::argValue( const QString & value )
{
    return arg( escapeString( value ) );
}

Db::DbString Db::DbString::argValue( const DbString & value )
{
    return arg( escapeString( value ) );
}

QString Db::DbString::escapeString( const QString & text )
{
    QString resultStr( "'" );
    QString::const_iterator it;
    FOREACH (it, text)
    {
        QMap<QChar, QString>::const_iterator iter = m_patternMap.find(  * it );
        if (iter == m_patternMap.end())
            resultStr.append( * it );
        else
            resultStr.append( iter.value() );
    }

    resultStr.append( "'" );
    return resultStr;
}

//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////

static bool processValues( const QByteArray & isNullValue, const QByteArray & value, Db::DbValue & outDbValue )
{
    bool ok = false;
    int number = isNullValue.toInt( & ok );
    if (! ok || number < 0 || number > 1)
        return false;

    outDbValue.setValue( number == 0 ? false : true, QByteArray::fromBase64( value ) );
    return true;
}

static bool processQueryResult( const QByteArray & resultData, Db::DbResultVector & dbResultVector )
{
    if (resultData.isEmpty())
        return true;

    QList<QByteArray> rowList = resultData.split( '|' );
    QList<QByteArray>::const_iterator it;
    FOREACH (it, rowList)
    {
        if (it->isEmpty())
            continue;

        Db::DbValueVector dbValueVector;
        QList<QByteArray>::const_iterator iter;
        QList<QByteArray> columnList = it->split( ';' );
        FOREACH (iter, columnList)
        {
            if (iter->isEmpty())
                continue;

            QList<QByteArray> valueList = iter->split( ':' );
            if (valueList.count() != 2)
                return false;

            QList<QByteArray>::const_iterator i = valueList.begin();
            Db::DbValue dbValue;
            if (! processValues( *valueList.begin(), *(++ i), dbValue ))
                return false;

            dbValueVector.push_back( dbValue );
        }

        dbResultVector.push_back( dbValueVector );
    }

    return true;
}

bool Db::select( const QString & sqlQuery, DbReader & dbReader )
{
    QByteArray resultData;
    HttpEngine & httpEngine = HttpEngine::httpEngine();
    if (! httpEngine.sendRequest( sqlQuery, HttpEngine::Select, resultData ))
    {
        showErrorBox( httpEngine.errorStr() );
        return false;
    }

    dbReader.reset();
    if (! processQueryResult( resultData, dbReader.dbResultVector() ))
    {
        showErrorBox( QObject::tr( "Unrecognized query output format" ) );
        return false;
    }

    return true;
}

bool Db::execute( const QString & sqlQuery )
{
    QByteArray resultData;
    HttpEngine & httpEngine = HttpEngine::httpEngine();
    if (! httpEngine.sendRequest( sqlQuery, HttpEngine::Dml, resultData ))
    {
        showErrorBox( httpEngine.errorStr() );
        return false;
    }

    return true;
}

//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////

Db::DbReader::DbReader()
:   m_currentRow( -1 )
{
}

Db::DbReader::~DbReader()
{
}

void Db::DbReader::reset()
{
    m_currentRow = -1;
    m_dbResultVector.clear();
}

Db::DbResultVector & Db::DbReader::dbResultVector()
{
    return m_dbResultVector;
}

int Db::DbReader::recordCount()
{
    return m_dbResultVector.count();
}

bool Db::DbReader::read()
{
    m_currentRow ++;
    return ( m_currentRow != m_dbResultVector.count() );
}

bool Db::DbReader::isNull( int column )
{
    return m_dbResultVector.at( m_currentRow ).at( column ).isNull();
}

int Db::DbReader::getInt( int column )
{
    return _getInt( m_dbResultVector.at( m_currentRow ).at( column ).value() );
}

double Db::DbReader::getDouble( int column )
{
    return _getDouble( m_dbResultVector.at( m_currentRow ).at( column ).value() );
}

QString Db::DbReader::getString( int column )
{
    return _getString( m_dbResultVector.at( m_currentRow ).at( column ).value() );
}

QDate Db::DbReader::getDate( int column )
{
    return _getDate( m_dbResultVector.at( m_currentRow ).at( column ).value() );
}

QDateTime Db::DbReader::getDateTime( int column )
{
    return _getDateTime( m_dbResultVector.at( m_currentRow ).at( column ).value() );
}

int Db::DbReader::getNvlInt( int column, int defaultValue )
{
    const Db::DbValue & dbValue = m_dbResultVector.at( m_currentRow ).at( column );
    if (dbValue.isNull())
        return defaultValue;
    
    return _getInt( dbValue.value() );
}

double Db::DbReader::getNvlDouble( int column, double defaultValue )
{
    const Db::DbValue & dbValue = m_dbResultVector.at( m_currentRow ).at( column );
    if (dbValue.isNull())
        return defaultValue;
    
    return _getDouble( dbValue.value() );
}

QString Db::DbReader::getNvlString( int column, const QString & defaultValue )
{
    const Db::DbValue & dbValue = m_dbResultVector.at( m_currentRow ).at( column );
    if (dbValue.isNull())
        return defaultValue;
    
    return _getString( dbValue.value() );
}

QDate Db::DbReader::getNvlDate( int column, const QDate & defaultValue )
{
    const Db::DbValue & dbValue = m_dbResultVector.at( m_currentRow ).at( column );
    if (dbValue.isNull())
        return defaultValue;
    
    return _getDate( dbValue.value() );
}

QDateTime Db::DbReader::getNvlDateTime( int column, const QDateTime & defaultValue )
{
    const Db::DbValue & dbValue = m_dbResultVector.at( m_currentRow ).at( column );
    if (dbValue.isNull())
        return defaultValue;
    
    return _getDateTime( dbValue.value() );
}

int Db::DbReader::_getInt( const QByteArray & value ) const
{
    int number  = 0;
    bool ok = false;
    number = value.toInt( & ok );
    if (! ok)
        assert( 0 );
    
    return number;
}

double Db::DbReader::_getDouble( const QByteArray & value ) const
{
    double number  = 0.0;
    bool ok = false;
    number = value.toDouble( & ok );
    if (! ok)
        assert( 0 );
    
    return number;
}

QString Db::DbReader::_getString( const QByteArray & value ) const
{
    return QString::fromUtf8( value );
}

QDate Db::DbReader::_getDate( const QByteArray & value ) const
{
    if (value.count() < 10)
        return QDate();

    QString dateStr( value.left( 10 ) );
    return QDate::fromString( dateStr, Qt::ISODate ); 
}

QDateTime Db::DbReader::_getDateTime( const QByteArray & value ) const
{
    QString dateStr( value );
    dateStr.replace( QChar( ' ' ), "T" );
    return QDateTime::fromString( dateStr, Qt::ISODate ); 
}
